package fr.jade.fraclite.type;

import org.objectweb.fractal.api.Type;
import org.objectweb.fractal.api.type.InterfaceType;

import java.io.Serializable;

/**
 * Provides a basic implementation of the {@link InterfaceType} interface.
 */
public class BasicInterfaceType implements InterfaceType, Serializable {

  /**
   * The {@link #flags flags} bit that indicates a client interface.
   */

  private final static int CLIENT_FLAG = 1;

  /**
   * The {@link #flags flags} bit that indicates an optional interface.
   */

  private final static int OPTIONAL_FLAG = 2;

  /**
   * The {@link #flags flags} bit that indicates a collection interface.
   */

  private final static int COLLECTION_FLAG = 4;

  /**
   * The name of the interface described by this type.
   */

  private String name;

  /**
   * The Java type of the interface described by this type.
   */

  private String signature;

  /**
   * Indicates if the interface described by this type is a client interface,
   * if it is optional...
   */

  private int flags;

  // -------------------------------------------------------------------------
  // Constructors
  // -------------------------------------------------------------------------

  /**
   * Constructs a new {@link BasicInterfaceType} object.
   *
   * @param name the name of the described interface.
   * @param signature the fully qualified name of the Java type of the described
   *      interface, or a Class object.
   * @param isClient <tt>true</tt> if the described interface is a client
   *      interface.
   * @param isOptional <tt>true</tt> if the described interface is an
   *      optional interface.
   * @param isCollection <tt>true</tt> if the described interface is a
   *      collection interface.
   */

  public BasicInterfaceType (String name, String signature, boolean isClient, boolean isOptional, boolean isCollection) {
    int flags = 0;
    flags = (isClient ? flags | CLIENT_FLAG : flags);
    flags = (isOptional ? flags | OPTIONAL_FLAG : flags);
    flags = (isCollection ? flags | COLLECTION_FLAG : flags);
    this.name = name;
    this.signature = signature;
    this.flags = flags;
  }

  // -------------------------------------------------------------------------
  // Implementation of the InterfaceType interface
  // -------------------------------------------------------------------------

  public String getFcItfName () {
    return name;
  }

  public String getFcItfSignature () {
    return signature;
  }

  public boolean isFcClientItf () {
    return (flags & CLIENT_FLAG) != 0;
  }

  public boolean isFcOptionalItf () {
    return (flags & OPTIONAL_FLAG) != 0;
  }

  public boolean isFcCollectionItf () {
    return (flags & COLLECTION_FLAG) != 0;
  }

  public boolean isFcSubTypeOf (final Type type) {
    if (type instanceof InterfaceType) {
      InterfaceType t = (InterfaceType)type;
      // role
      if (t.isFcClientItf() != isFcClientItf()) {
        return false;
      }
      // name
      if (!t.getFcItfName().equals(getFcItfName())) {
        return false;
      }
      // cardinality
      if (t.isFcCollectionItf() && !isFcCollectionItf()) {
        return false;
      }
      // contingency
      if (isFcClientItf()) {
        if (t.isFcOptionalItf() && !isFcOptionalItf()) {
          return false;
        }
      } else {
        if (!t.isFcOptionalItf() && isFcOptionalItf()) {
          return false;
        }
      }
      // signature
      if (t.getFcItfSignature().equals(getFcItfSignature())) {
        return true;
      }
      if (t instanceof BasicInterfaceType) {
        try {
          Class c1 = Class.forName(signature);
          Class c2 = Class.forName(((BasicInterfaceType)t).signature);
          if (c1 != null && c2 != null && isFcClientItf()) {
            return c1.isAssignableFrom(c2);
          } else {
            return c2.isAssignableFrom(c1);
          }
        } catch (ClassNotFoundException e) {
          return true;
        }
      }
    }
    return false;
  }

  // -------------------------------------------------------------------------
  // Overriden Object methods
  // -------------------------------------------------------------------------

  /**
   * Tests if this interface type is equal to the given object.
   *
   * @param o the object to be compared to this interface type.
   * @return <tt>true</tt> if this interface type is equal to the given object.
   */

  public boolean equals (final Object o) {
    if (o instanceof InterfaceType) {
      InterfaceType type = (InterfaceType)o;
      if (getFcItfName().equals(type.getFcItfName()) &&
        isFcClientItf() == type.isFcClientItf() &&
        isFcOptionalItf() == type.isFcOptionalItf() &&
        isFcCollectionItf() == type.isFcCollectionItf())
      {
        if (getFcItfSignature().equals(type.getFcItfSignature())) {
          return true;
        }
      }
    }
    return false;
  }
  
  public String toString(){
    String role = isFcClientItf()?"client":"server";
    String cardinality = isFcCollectionItf()?"collection":"single";
    String contingency = isFcOptionalItf()?"optional":"mandatory";
    return  name + " " + signature + " " + role + " " + cardinality + " " + contingency;
  }
}
